package highscore;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import utils.InvalidEncryptedStringException;
import utils.HashMaker;
import java.util.Scanner;
import java.util.Date;
import java.util.Vector;

/**
 * Used to write a highscore to a file.
 * 
 * @author Swedman Skomakar'n
 */
public class HighscoreFileHandler
{
    /**
     * Variable to save the Highscore associated with this HighscoreFileHandler in
     */
    private Highscore highscore;

    /**
     * Constructor which will create a new Highscore
     * You can use getHighscore() to get the associated highscore
     * 
     * @see HighscoreFileHandler#getHighscore()
     */
    public HighscoreFileHandler()
    {
		// The reason this use null and not new Highscore is because the constructor accepts null and automaticly creates a new Highscore
		// The reason it does that is because if anyone uses null as a parameter it shouldn't become unstable
		this(null);
    }

    /**
     * Constructor.
     * 
     * @param highscore The highscore to manipulate.
     */
    public HighscoreFileHandler(Highscore highscore)
    {
		if (highscore == null)
			this.highscore = new Highscore();
		else
			this.highscore = highscore;
    }

    /**
     * Get the highscore object.
     *
     * @return The highscore object.
     */
    public Highscore getHighscore()
    {
        return highscore;
    }

    /**
     * Saves the Highscore to a file.
     *
     * @param file Where to save it
     */
    public void saveHighscore(String file)
    {
		saveHighscore(new File(file));
    }

    /**
     * Saves the Highscore to a file.
     *
     * @param file The file to save to.
     */
    public void saveHighscore(File file)
    {
		// File.canWrite only works if the file already exists
		if (file.exists() && !file.canWrite())
		{
			System.err.println("Failed to save the highscore (couldn't overwrite existing file)");
			return;
		}

        try
        {
            PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file)));

            Vector<String> text = encryptHighscore(highscore);

            for (int i = 0; i < text.size(); i ++)
                writer.println(text.get(i));

            writer.close();
        }
        catch (IOException e)
        {
            System.err.println("Failed to save the highscore (unknown IO error)");
        }
    }

    /**
     * Loads a Highscore from a file.
     * @param file The file to load from
     */
    public void loadHighscore(String file)
    {
		loadHighscore(new File(file));
    }

    /**
     * Loads a Highscore from a file.
     *
     * @param file The file to load from
     */
    public void loadHighscore(File file)
    {
		if (!file.exists())
		{
			System.err.println("Failed to load the highscore (file does not exist)");
			return;
		}

		if (!file.canRead())
		{
			System.err.println("Failed to load the highscore (file is not readable)");
			return;
		}

        try
        {
            Scanner scanner = new Scanner(file);

            String text = "";

            while (scanner.hasNext())
                text += scanner.nextLine() + "\n";

            scanner.close();

            try
            {
                highscore.setHighscore(decryptHighscore(text));
            }
            catch (InvalidEncryptedStringException e)
            {
                System.err.println("Failed to load the highscore (invalid file or corrupt data)");
            }
        }
        catch (FileNotFoundException ex)
        {
            System.err.println("Failed to load the highscore. (missing file)");
        }
    }

    /**
     * Encrypts a Highscore to a string
     *
     * @param h The Highscore to encrypt
     * @return The encrypted string
     */
    private Vector<String> encryptHighscore(Highscore highscore)
    {
        Vector<String> result = new Vector<String>();

        for (int i = 0; i < highscore.getRowCount(); i ++)
        {
            HighscoreRow row = highscore.getRowAt(i);

            result.add(HashMaker.encrypt(row.getName()));
            result.add(HashMaker.encrypt(Integer.toString(row.getLevel())));
            result.add(HashMaker.encrypt(Integer.toString(row.getScore())));
            result.add(HashMaker.encrypt(Long.toString(row.getDate().getTime())));
        }

       return result;
    }

    /**
     * Decrypts a string to a Highscore
     *
     * @param str The String to decrypt
     * @return The Highscore which was encrypted
     * @throws InvalidEncryptedException if the String in not a valid encrypted String
     */
    private Highscore decryptHighscore(String string) throws InvalidEncryptedStringException
    {
        if (string == null || string.length() == 0)
            throw new InvalidEncryptedStringException();

        boolean c = false;

        for (int i = 0; i < string.length(); i ++)
            if ((int)string.charAt(i) >= 48 && (int)string.charAt(i) <= 57)
                c = true;

        if (!c)
            throw new InvalidEncryptedStringException();

        Highscore result = new Highscore();
        Scanner scanner = new Scanner(string);

        while (scanner.hasNext())
        {
            String name = HashMaker.decrypt(scanner.nextLine());
            int level = Integer.parseInt(HashMaker.decrypt(scanner.nextLine()));
            int score = Integer.parseInt(HashMaker.decrypt(scanner.nextLine()));
            Date date = new Date(Long.parseLong(HashMaker.decrypt(scanner.nextLine())));

            result.addItemByScore(new HighscoreRow(name,level,score,date));
        }

        scanner.close();

        return result;
    }
}
